﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;

namespace Useful.Sys
{
    public class SystemHelper
    {
        public static void ShowFile(string fileName, int sleepAfter)
        {
            ProcessStartInfo psi = new ProcessStartInfo(fileName);
            psi.Verb = "Open";
            Process.Start(psi);
            Thread.Sleep(sleepAfter);
        }

        public static void ShowFile(string fileName)
        {
            ShowFile(fileName, 0);
        }

        public static void PrintFile(string fileName, string printerName, int sleepAfter)
        {
            ProcessStartInfo psi = new ProcessStartInfo(fileName);
            psi.Verb = "Printto";
            psi.Arguments = printerName;
            Process.Start(psi);
            Thread.Sleep(sleepAfter);
        }

        public static void PrintFile(string fileName, string printerName)
        {
            PrintFile(fileName, printerName, 0);
        }

        public static void ExecProcess(string fileName, string args, int sleepAfter)
        {
            ProcessStartInfo psi = new ProcessStartInfo(fileName, args);
            Process.Start(psi);
            Thread.Sleep(sleepAfter);
        }

        public static void ExecProcess(string fileName, string args)
        {
            ExecProcess(fileName, args, 0);
        }

        public static void ExecProcess(string fileName)
        {
            ExecProcess(fileName, null, 0);
        }

        public static bool TestExistFile(string fullFileName)
        {
            bool res = true;

            if (File.Exists(fullFileName))
            {
                if (MessageBoxHelper.ShowQuestion(String.Format("Файл с таким именем уже существует: '{0}', перезаписать его новым?", fullFileName)))
                    File.Delete(fullFileName);
                else
                    res = false;
            }

            return res;
        }

        public static bool TestExistFile(string fullFileName, bool deleteOldFile)
        {
            if (File.Exists(fullFileName) && deleteOldFile)
                File.Delete(fullFileName);

            return File.Exists(fullFileName);
        }

        public static bool WaitFileReady(string fileName, int timeOut, int attemptCount)
        {
            int attempt = attemptCount;

            while (attempt > 0)
            {
                try
                {
                    using (FileStream fs = File.Open(fileName, FileMode.Open))
                    {
                        fs.Close();
                    }

                    return true;
                }
                catch
                {
                    Thread.Sleep(timeOut);
                }

                attempt--;
            }

            return false;
        }

        public delegate void ActionDelegate();
        public delegate void ExceptionDelegate(Exception ex);

        public static void WaitFileAction(int timeOut, int attemptCount, ActionDelegate action, ExceptionDelegate exception)
        {
            bool res = false;
            int attempt = attemptCount;

            Exception lastEx = null;

            while (attempt > 0 && !res)
            {
                try
                {
                    action();

                    res = true;
                }
                catch(Exception ex)
                {
                    lastEx = ex;
                    Thread.Sleep(timeOut);
                }

                attempt--;
            }

            if (!res)
                exception(lastEx);
        }

        public static string GetPathWithoutDrive(string path)
        {
            string res = null;
            string drive = Path.GetPathRoot(path);

            if (!String.IsNullOrEmpty(drive))
                res = path.Remove(0, drive.Length);
            else
                res = path;

            return res;
        }

        public static string GetNetwotrkPath(string path)
        {
            return Path.Combine("\\\\", path);
        }

        public static string GetNetworkFullFileName(string host, string path, string shortFileName)
        {
            string networkFullFileName = Path.Combine(host, GetPathWithoutDrive(path));

            if (shortFileName != null)
                networkFullFileName = Path.Combine(networkFullFileName, shortFileName);

            networkFullFileName = GetNetwotrkPath(networkFullFileName);

            return networkFullFileName;
        }

        public static string GetCombinedPath(params string[] args)
        {
            string res = String.Empty;

            foreach (string arg in args)
                res = Path.Combine(res, arg);

            return res;
        }

        public static string GetOnlyFileName(string fullPath)
        {
            return Path.GetFileNameWithoutExtension(fullPath);
        }

        public static string GetFullFileName(string folderName, string shortFileName)
        {
            return Path.Combine(folderName, shortFileName);
        }

        public static string RemoveExt(string fileName)
        {
            return Path.ChangeExtension(fileName, String.Empty);
        }

        public static string[] ExtractZipFile(string archiveFilenameIn, string password, string outFolder, bool overrideExistingFile)
        {
            List<string> outputFullFileNameList = new List<string>();

            ZipFile zf = null;
            try
            {
                FileStream fs = File.OpenRead(archiveFilenameIn);
                zf = new ZipFile(fs);
                if (!String.IsNullOrEmpty(password))
                {
                    zf.Password = password;		// AES encrypted entries are handled automatically
                }
                foreach (ZipEntry zipEntry in zf)
                {
                    if (!zipEntry.IsFile)
                    {
                        continue;			// Ignore directories
                    }
                    String entryFileName = zipEntry.Name;
                    // to remove the folder from the entry:- entryFileName = Path.GetFileName(entryFileName);
                    // Optionally match entrynames against a selection list here to skip as desired.
                    // The unpacked length is available in the zipEntry.Size property.

                    byte[] buffer = new byte[4096];		// 4K is optimum
                    Stream zipStream = zf.GetInputStream(zipEntry);

                    // Manipulate the output filename here as desired.
                    String fullZipToPath = Path.Combine(outFolder, entryFileName);
                    string directoryName = Path.GetDirectoryName(fullZipToPath);
                    if (directoryName.Length > 0)
                        Directory.CreateDirectory(directoryName);

                    if (overrideExistingFile || !File.Exists(fullZipToPath))
                    {
                        // Unzip file in buffered chunks. This is just as fast as unpacking to a buffer the full size
                        // of the file, but does not waste memory.
                        // The "using" will close the stream even if an exception occurs.
                        using (FileStream streamWriter = File.Create(fullZipToPath))
                        {
                            StreamUtils.Copy(zipStream, streamWriter, buffer);
                            outputFullFileNameList.Add(fullZipToPath);
                        }
                    }
                    else
                        throw new IOException(String.Format("Файл с именем '{0}' уже существует", fullZipToPath));
                }
            }
            finally
            {
                if (zf != null)
                {
                    zf.IsStreamOwner = true; // Makes close also shut the underlying stream
                    zf.Close(); // Ensure we release resources
                }
            }

            return outputFullFileNameList.ToArray();
        }
    }
}
